package logic;

import interfaze.BrickBreakerGamingPanel;
import interfaze.BrickBreakerLeaderBoardPanel;
import interfaze.SuperArcade2in1;

import java.util.ArrayList;

import javax.swing.JPanel;

import constants.BonusType;
import constants.GameTables;
import constants.PercentNumber;
import constants.WallType;

import entities.AmmunitionCounter;
import entities.Ball;
import entities.Bat;
import entities.Bonus;
import entities.Brick;
import entities.Level;
import entities.Life;
import entities.Rocket;
import entities.Score;
import entities.Wall;

/** Brick Breake's Collision logic class.
 * @author Team 1
 *
 */
public class CollisionThread extends Thread {
	
	private ArrayList<Wall> wallList;				//the list of walls placed on the playground.
	private ArrayList<Ball> ballList;				//the list of balls placed on the playground.
	private ArrayList<Rocket> rocketList;			//the list of rockets placed on the playground.
	private ArrayList<Bonus> bonusList;				//the list of bonus placed on the playground.
	private ArrayList<Brick> brickList;				//the list of brick placed on the playground.
	private ArrayList<MovementThread> threadList;	//the list of movement threads.
	private BatLimiterThread limiter;				//the limiter thread.
	private Bat bat;								//the bat.
	private JPanel panel;							//the playground panel.
	private SuperArcade2in1 window;					//The window in which the playground is placed.
	private int ballSpeed;							//The ball speed.
	private int bonusSpeed;							//the bonus speed.
	private int batSpeed;							//the bat speed.
	private int rowNumber;							//The brick's row number.
	private int columnNumber;						//The brick's column number.
	private int brickLives;							//The birck's lives number.
	private Level level;							//The current level number.
	private Life lives;								//The lives number.
	private Score points;							//The achieved points number.
	private AmmunitionCounter counter;				//Ammunition counter.
	private final PercentNumber percent;			//The percent to create bonus.
	private boolean abort = false;					//The flag to abort this thread.
	
	/** CollisionThread's constructor.
	 * @param ballList : the list of balls placed on the playground.
	 * @param brickList : the list of brick placed on the playground.
	 * @param bonusList : the list of bonus placed on the playground.
	 * @param rocketList : the list of rockets placed on the playground.
	 * @param wallList : the list of walls placed on the playground.
	 * @param threadList : the list of movement threads.
	 * @param limiter : the limiter thread.
	 * @param bat : the bat.
	 * @param panel : the playground panel.
	 * @param window : The window in which the playground is placed.
	 * @param ballSpeed : The ball speed.
	 * @param bonusSpeed : the bonus speed.
	 * @param batSpeed : the bat speed.
	 * @param percent : The percent to create bonus.
	 * @param lives : The lives number.
	 * @param points : The achieved points number.
	 * @param counter : Ammunition counter.
	 * @param level : The current level number.
	 * @param brickLives : The birck's lives number.
	 * @param columnNumber : The brick's column number.
	 * @param rowNumber : The brick's row number.
	 */
	public CollisionThread(ArrayList<Ball> ballList, ArrayList<Brick> brickList, ArrayList<Bonus> bonusList,
			ArrayList<Rocket> rocketList, ArrayList<Wall> wallList, ArrayList<MovementThread> threadList, BatLimiterThread limiter,
			Bat bat, JPanel panel, SuperArcade2in1 window, int ballSpeed, int bonusSpeed, int batSpeed, PercentNumber percent,
			Life lives, Score points, AmmunitionCounter counter, Level level, int brickLives, int columnNumber, int rowNumber) {
		super();
		
		//Initializes the variables.
		this.panel = panel;
		this.window = window;
		this.ballList = ballList;
		this.brickList = brickList;
		this.bonusList = bonusList;
		this.rocketList = rocketList;
		this.wallList = wallList;
		this.threadList = threadList;
		this.limiter = limiter;
		this.bat = bat;
		this.ballSpeed = ballSpeed;
		this.bonusSpeed = bonusSpeed;
		this.batSpeed = batSpeed;
		this.percent = percent;
		this.lives = lives;
		this.points = points;
		this.counter = counter;
		this.level = level;
		this.columnNumber = columnNumber;
		this.rowNumber = rowNumber;
		this.brickLives = brickLives;
	}
	
	public void run() {
		while(!Thread.currentThread().isInterrupted() && !abort && (ballList.get(0).canMove() || bonusList.size() > 0 || rocketList.size() > 0)) {
			for(int i = 0; i < bonusList.size(); i++) {
				//Applies the bonus effect and removes it when intersects with bat's upper, left part or right part.
				if(bonusList.get(i).intersects(bat)[1] || bonusList.get(i).intersects(bat)[2] || bonusList.get(i).intersects(bat)[3]) {
					switch(bonusList.get(i).getType()) {
					case DOUBLE_BALL:
						//Duplicates the number of balls placed on the playground.
						if(ballList.get(0).canMove() && ballList.size() * 2 <= 75) {
							int size = ballList.size();
							for(int j = 0; j < size; j++) {
								Ball duplicatedBall = new Ball(ballList.get(j).getX(), ballList.get(j).getY(),
									Math.round(ballList.get(j).getDX() * -1), Math.round(ballList.get(j).getDY() * -1),
									window.getXscale(), window.getYscale());
								panel.add(duplicatedBall, 0);
								ballList.add(duplicatedBall);
								MovementThread thread = new MovementThread(duplicatedBall);
								thread.start();
								threadList.add(thread);
							}
						}
						break;
					case POINTS_BOOSTER:
						//Adds 500 points to the score.
						points.setPoints(points.getPoints() + 500);
						break;
					case SHOOTER:
						//Adds 3 bullets to the bat's current ammo.
						bat.setAmmo(bat.getAmmo() + 3);
						counter.setAmmoCounter(bat.getAmmo());
						break;
					}
					//Removes the bonus.
					bonusList.get(i).setCanMove(false);
					panel.remove(bonusList.get(i));
					panel.repaint(bonusList.get(i).getX(), bonusList.get(i).getY(), bonusList.get(i).getWidth(), bonusList.get(i).getHeight());
					bonusList.remove(i);
				}  else if(bonusList.get(i).getY() > window.getHeight()) {
					//Removes bonuses when they get out of the Boundaries.
					bonusList.get(i).setCanMove(false);
					panel.remove(bonusList.get(i));
					panel.repaint(bonusList.get(i).getX(), bonusList.get(i).getY(), bonusList.get(i).getWidth(), bonusList.get(i).getHeight());
					bonusList.remove(i);
				}
			}
			
			for(int i = 0; i < ballList.size(); i++) {
				for(int j = 0; j < wallList.size(); j++) {
					//Changes the direction of the ball when it intersect with a wall.
					if(ballList.get(i).intersects(wallList.get(j))[0] && wallList.get(j).getType().equals(WallType.UPWALL)) {
						ballList.get(i).setLocation(ballList.get(i).getX(), wallList.get(j).getY() + wallList.get(j).getHeight() + 1);
						ballList.get(i).setDY(Math.round(ballList.get(i).getDY() * -1));
					} else if(ballList.get(i).intersects(wallList.get(j))[2] && wallList.get(j).getType().equals(WallType.RIGHTWALL)) {
						ballList.get(i).setLocation(wallList.get(j).getX() - (ballList.get(i).getWidth() + 1), ballList.get(i).getY());
						ballList.get(i).setDX(Math.round(ballList.get(i).getDX() * -1));
					} else if(ballList.get(i).intersects(wallList.get(j))[3] && wallList.get(j).getType().equals(WallType.LEFTWALL)) {
						ballList.get(i).setLocation(wallList.get(j).getX() + wallList.get(j).getWidth() + 1, ballList.get(i).getY());
						ballList.get(i).setDX(Math.round(ballList.get(i).getDX() * -1));
					}
				}
				//Changes the direction of the ball when it intersect with a bat.
				if(ballList.get(0).canMove()) {
					if(ballList.get(i).intersects(bat)[1]) {
						ballList.get(i).setLocation(ballList.get(i).getX(), bat.getY() - (ballList.get(i).getHeight() + 1));
						ballList.get(i).setDY(Math.round(ballList.get(i).getDY() * -1));
					} else if(ballList.get(i).intersects(bat)[2]) {
						if(ballList.get(i).getDX() > 0) {
							ballList.get(i).setDX(Math.round(ballList.get(i).getDX() * -1));
						}
					} else if(ballList.get(i).intersects(bat)[3]) {
						if(ballList.get(i).getDX() < 0) {
							ballList.get(i).setDX(Math.round(ballList.get(i).getDX() * -1));
						}
					}
				}
				for(int j = 0; j < brickList.size(); j++) {
					boolean intersect = false;
					//Changes the direction of the ball when it intersect with a brick.
					if(ballList.get(i).intersects(brickList.get(j))[0] || ballList.get(i).intersects(brickList.get(j))[1]) {
						if(ballList.get(i).getDY() > 0) {
							ballList.get(i).setDY(Math.round(-ballSpeed * window.getYscale()));
							ballList.get(i).setLocation(ballList.get(i).getX(), ballList.get(i).getY() - Math.round(8 * window.getYscale()));
						} else {
							ballList.get(i).setDY(Math.round(ballSpeed * window.getYscale()));
							ballList.get(i).setLocation(ballList.get(i).getX(), ballList.get(i).getY() + Math.round(8 * window.getYscale()));
						}
						if(ballList.get(i).getDX() > 0) {
							ballList.get(i).setDX(Math.round(ballSpeed * window.getXscale()));
						}else {
							ballList.get(i).setDX(Math.round(-ballSpeed * window.getXscale()));
						}
						intersect = true;
					} if(ballList.get(i).intersects(brickList.get(j))[2] || ballList.get(i).intersects(brickList.get(j))[3]) {	
						if(ballList.get(i).getDX() > 0) {
							ballList.get(i).setDX(Math.round(-ballSpeed * window.getXscale()));
							ballList.get(i).setLocation(ballList.get(i).getX() - Math.round(8 * window.getXscale()), ballList.get(i).getY());
						} else {
							ballList.get(i).setDX(Math.round(ballSpeed * window.getXscale()));
							ballList.get(i).setLocation(ballList.get(i).getX() + Math.round(8 * window.getXscale()), ballList.get(i).getY());
						}
						if(ballList.get(i).getDY() > 0) {
							ballList.get(i).setDY(Math.round(-ballSpeed * window.getYscale()));
						} else {
							ballList.get(i).setDY(Math.round(ballSpeed * window.getYscale()));
						}
						intersect = true;
					}
					if(intersect) {
						//Damages brick subtracting -1 to its life counter.
						brickList.get(j).setLifes(brickList.get(j).getLives() - 1);
						//50 points will also be added to score counter
						points.setPoints(points.getPoints() + 50);
						//If brick's life reach zero it'll be removed.
						if(brickList.get(j).getLives() == 0) {	
							//25 points bonus when destroyed.
							points.setPoints(points.getPoints() + 25);
							//Creates a bonus depending on the percent value.
							int percentNum = Utilities.percentToRandomParser(percent);
							switch(percentNum) {
								case 0:
									Bonus bonus = null;
									MovementThread thread = null;
									//Creates a random type bonus.
									BonusType type = Utilities.randomBonusType();
									switch(type) {
									case DOUBLE_BALL:
										//Generates a Double ball bonus if the placed ball list size is 
										if(ballList.size() * 2 <= 75) {
											bonus = new Bonus(brickList.get(j).getX(), brickList.get(j).getY(),
													0, Math.round(bonusSpeed * window.getYscale()),
													BonusType.DOUBLE_BALL, window.getXscale(), window.getYscale());
											panel.add(bonus, 0);
											bonusList.add(bonus);
											thread = new MovementThread(bonus);
											thread.start();
											threadList.add(thread);
										}
										break;
									case POINTS_BOOSTER:
										//Generates a points booster bonus.
										bonus = new Bonus(brickList.get(j).getX(), brickList.get(j).getY(), 
												0, Math.round(bonusSpeed * window.getYscale()),
												BonusType.POINTS_BOOSTER, window.getXscale(), window.getYscale());
										panel.add(bonus, 0);
										bonusList.add(bonus);
										thread = new MovementThread(bonus);
										thread.start();
										threadList.add(thread);
										break;
									case SHOOTER:
										//Generates a shooter bonus.
										bonus = new Bonus(brickList.get(j).getX(), brickList.get(j).getY(),
												0, Math.round(bonusSpeed * window.getYscale()),
												BonusType.SHOOTER, window.getXscale(), window.getYscale());
										panel.add(bonus, 0);
										bonusList.add(bonus);
										thread = new MovementThread(bonus);
										thread.start();
										threadList.add(thread);
										break;
									}
								break;
							}
							//Removes the brick from the panel.
							panel.remove(brickList.get(j));
							panel.repaint(brickList.get(j).getX(), brickList.get(j).getY(),
									brickList.get(j).getWidth(), brickList.get(j).getHeight());
							brickList.remove(j);
						}
					}
				}
			}
			//Initialize ball when gets out of screen boundaries.
			if(ballList.size() == 1 && (ballList.get(0).getY() > window.getHeight() || ballList.get(0).getY() < 0 || ballList.get(0).getX() < 0
					|| ballList.get(0).getX() > window.getWidth())) {
				ballList.get(0).setLocation((bat.getX() + bat.getWidth() / 2) - (ballList.get(0).getWidth() / 2),
						bat.getY() - (ballList.get(0).getHeight() + Math.round(1 * window.getYscale())));
				ballList.get(0).setCanMove(false);
				lives.setLives(lives.getLives() - 1);
			}else {
				//Removes balls if they get out of the screen boundaries.
				for(int i = 1; i < ballList.size(); i++) {
					if(ballList.get(i).getY() > window.getHeight() || ballList.get(i).getY() < 0 || ballList.get(i).getX() < 0
							|| ballList.get(i).getX() > window.getWidth()) {
						ballList.get(i).setCanMove(false);
						panel.remove(ballList.get(i));
						panel.repaint(ballList.get(i).getX(), ballList.get(i).getY(), ballList.get(i).getWidth(), ballList.get(i).getHeight());
						ballList.remove(i);
					}
				}
			}
			ArrayList<Rocket> deleteList = new ArrayList<Rocket>();
			for(int i = 0; i < rocketList.size(); i++) {
				//Remove the rocket if it intersect with the wall.
				if(rocketList.get(i).intersects(wallList.get(1))[0]) {
					rocketList.get(i).setCanMove(false);
					deleteList.add(rocketList.get(i));
					panel.remove(rocketList.get(i));
					panel.repaint(rocketList.get(i).getX(), rocketList.get(i).getY(), rocketList.get(i).getWidth(), rocketList.get(i).getHeight());
				} else {
					for(int j = 0; j < brickList.size(); j++) {
						//Remove rockets and bricks if they intersect each other.
						if(rocketList.get(i).intersects(brickList.get(j))[0]) {
							//Add 25 points when brick is destroyed.
							points.setPoints(points.getPoints() + 25);
							rocketList.get(i).setCanMove(false);
							deleteList.add(rocketList.get(i));
							panel.remove(rocketList.get(i));
							panel.repaint(rocketList.get(i).getX(), rocketList.get(i).getY(), 
									rocketList.get(i).getWidth(), rocketList.get(i).getHeight());
							panel.remove(brickList.get(j));
							panel.repaint(brickList.get(j).getX(), brickList.get(j).getY(), 
									brickList.get(j).getWidth(), brickList.get(j).getHeight());
							brickList.remove(brickList.get(j));
						}
					}
				}
			}
			for(int i = 0; i < deleteList.size(); i++) {
				rocketList.remove(deleteList.get(i));
			}
			if(lives.getLives() == -1) {
				//Redirects to the leader board panel after inserting the record into the database.
				panel.setVisible(false);
				database.Insert.insertRecord(window.getUserLogged(), points.getPoints(), GameTables.BRICKBREAKER);
				window.add(new BrickBreakerLeaderBoardPanel(window, "GamePanel"));
				window.remove(panel);
				window.validate();
				abort = true;
			}
			if(brickList.size() == 0) {
				//Creates a new playground with the current points, lives, ammo and adds one to the level and to the brick's lives.
				window.add(new BrickBreakerGamingPanel(window, false, lives.getLives(), points.getPoints(), bat.getAmmo(), rowNumber, columnNumber,
						ballSpeed, batSpeed, bonusSpeed, percent, brickLives + 1, level.getLevel() + 1));
				panel.setVisible(false);
				window.remove(panel);
				window.validate();
				abort = true;
			}
		}
		if(abort) {
			//Stops all the threads.
			for(int i = 0; i < threadList.size(); i++) {
				threadList.get(i).interrupt();
				threadList.remove(threadList.get(i));
			}
			if(limiter != null) {
				limiter.interrupt();
			}
		}
		this.interrupt();
	}
}
